﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

/*******************18.c语言内存函数*******************/
/***1. memcpy使⽤和模拟实现***/
/*函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠，复制的结果都是未定义的。*/

//int main()
//{
//	/*int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[10] = { 0 };
//	memcpy(arr2, arr1, 20);*/
//
//	/*double d1[8] = { 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0 };
//	double d2[8] = { 0 };
//	memcpy(d2, d1+1, 4 * sizeof(double));*/
//
//	double d1[8] = { 1.0,2.0,3.0,4.0,5.0,6.0 };
//	//不保证重叠内存的拷贝，通常拷贝不重叠内存的数据
//	memcpy(d1 + 3, d1 + 1, 4 * sizeof(double));
//	
//	return 0;
//}

//int main()
//{
//	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[10] = { 0 };
//
//	memcpy(arr2, arr1, 5 * sizeof(int));
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr2[i]);
//	}
//
//	return 0;
//}

//mecmcpy模拟实现
//#include <assert.h>
//
//void* my_memcpy(void* dest, const void* src, size_t num)
//{
//	assert(dest && src);
//	void* ret = dest;
//	while (num--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
//	return ret;
//}
//
//int main()
//{
//	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	//int arr2[10] = { 0 };
//
//	//my_memcpy(arr2, arr1, 5 * sizeof(int));
//	my_memcpy(arr1+2, arr1, 5 * sizeof(int));//内存重叠拷贝
//
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		//printf("%d ", arr2[i]);
//		printf("%d ", arr1[i]);////内存重叠拷贝
//
//	}
//
//	return 0;
//}

/***2. memmove 使⽤和模拟实现***/
//模拟实现 
//#include <assert.h>
//
//void* my_memmove(void* dest, void* src, size_t num)
//{
//	assert(dest && src);
//	void* ret = dest;
//	if (dest < src)
//	{
//		//前->后
//		while (num--)
//		{
//			*(char*)dest = *(char*)src;
//			dest = (char*)dest + 1;
//			src = (char*)src + 1;
//		}
//		return ret;
//	}
//	else
//	{
//		//后->前
//		while (num--)
//		{
//			*((char*)dest + num) = *((char*)src + num);
//		}
//	}
//	return ret;
//}
//
//int main()
//{
//	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[10] = { 0 };
//	//memmove(arr2, arr1, 5*sizeof(int));
//	my_memmove(arr1+2, arr1, 5 * sizeof(int));
//
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		//printf("%d ", arr2[i]);
//		printf("%d ", arr1[i]);////内存重叠拷贝
//
//	}
//	return 0;
//}

/***3. memset函数的使⽤***/

//int main()
//{
//	/*char arr[10] = { 0 };
//	memset(arr, 'x', 5);*/
//
//	int arr[10] = {0};//不是1 1 1 1 1 0 0 0 0 0
//	memset(arr, 1, 5);
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//
//	return 0;
//}

/***4. memcmp 函数的使⽤***/

//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[10] = { 1,2,3,4 };
//
//	int r = memcmp(arr1, arr2, 16);
//	printf("%d\n",r);
//	return 0;
//}

/*******************19.数据在内存中的存储*******************/
/***1. 整数在内存中的存储***/
//对于整形来说：数据存放内存中其实存放的是⼆进制的补码
//正整数的原、反、补码都相同。
//负整数的三种表⽰⽅法各不相同

/***2. ⼤⼩端字节序和字节序判断***/

//int main()
//{
//	int a = 0x11223344;//vs2022是小端字节存储
//		//高地址    低地址
//	//⼤端字节存储：是指数据的低位字节内容保存在内存的⾼地址处，⽽数据的⾼位字节内容，保存在内存的低地址处
//	//⼩端字节存储：是指数据的低位字节内容保存在内存的低地址处，⽽数据的⾼位字节内容，保存在内存的⾼地址处。
//	return 0;
//}

/*2.1练习1.设计⼀个⼩程序来判断当前机器的字节序*/
//int main()
//{
//	int a = 1;
//	if (*(char*)&a == 1)
//		printf("小端");
//	else
//		printf("大端");
//	return 0;
//}

//int check_sys()
//{
//	int a = 1;
//	return *(char*)&a;
//}
//
//int main()
//{
//	if (check_sys() == 1)
//		printf("小端");
//	else
//		printf("大端");
//		return 0;
//}

/*练习2*/
//int main()
//{
//	//char类型变量取值范围 -128~127
//	char a = -1;
//	//10000000 00000000 00000000 00000001 -原码
//	//11111111 11111111 11111111 11111110 -反码
//	//11111111 11111111 11111111 11111111 -补码
//	//11111111 -a存的
//	//11111111 11111111 11111111 11111111 整型提升，打印原码
//
//	signed char b = -1;
//	//11111111 -b存的
//	//11111111 11111111 11111111 11111111 整型提升，打印原码
//
//	unsigned char c = -1;//无符号提升高位补0
//	//11111111 -c存的
//	//00000000 00000000 00000000 11111111 整型提升，打印原码
//	//正数原反补相同
//
//	printf("a=%d,b=%d,c=%d", a, b, c);//-1,-1,255
//	//%d - 以10进制的形式打印有符号的整数
//
//	return 0;
//}

//int main()
//{
//	char a = -128;
//	//10000000 00000000 00000000 10000000
//	//11111111 11111111 11111111 01111111
//	//11111111 11111111 11111111 10000000
//	//10000000 - a
//	//11111111 11111111 11111111 10000000 -整型提升,补符号位
//	printf("%u\n", a);
//	//%u以10进制打印无符号整数
//	
//	return 0;
//}

//int main()
//{
//	char a = 128;
//	//00000000 00000000 00000000 10000000
//	//10000000 - a
//
//	printf("%u\n", a);
//	return 0;
//}

//int main()
//{
//	char a[1000];
//	int i;
//	for (i = 0; i < 1000; i++)
//	{
//		a[i] = -1 - i;
//	}
//	printf("%d",strlen(a));
//	return 0;
//}

//unsigned char i = 0; //0~255
//int main()
//{
//	for (i = 0; i <= 255; i++) //i <=255恒成立
//	{
//		printf("hello world\n");//死循环
//	}
//	return 0;
//}

//int main()
//{
//	unsigned int i;//0~42亿+
//	for (i = 9; i >= 0; i--)//i >= 0恒成立
//	{
//		printf("%u\n", i);//死循环
//	}
//	return 0;
//}

////X86环境 ⼩端字节序
//int main()
//{
//	int a[4] = { 1, 2, 3, 4 };
//	int* ptr1 = (int*)(&a + 1);//(&a + 1)跳过整个数组
//	int* ptr2 = (int*)((int)a + 1);//a是地址，转成整型+1，既跳过一个字节
//
//	printf("%x,%x", ptr1[-1], *ptr2);
//	//%x16进制的形式打印
//	//ptr1[-1]数组倒数第一个元素
//	//*ptr2000000
//	return 0;
//}

/***3. 浮点数在内存中的存储***/
/*整数和浮点数在内存中存储方式不同
int main()
{
	int n = 9;
	//00000000 00000000 00000000 00001001原
	//00000000 00000000 00000000 00001001反
	//00000000 00000000 00000000 00001001补

	float* pFloat = (float*)&n;
	printf("n的值为：%d\n", n); //9
	printf("*pFloat的值为：%f\n", *pFloat);
	//0 00000000 0000000 00000000 00001001
	//e=-126
	//s=0
	//m=0.0000000 00000000 00001001
	//(-1)^0 * 2^-126 * 0.0000000 00000000 00001001

	*pFloat = 9.0;
	//1001.0
	//(-1)^0 * 1.001 * 2^3
	//s=0
	//m=1.001
	//e=3
	//0 10000010 0010000000000000000000000

	printf("n的值为：%d\n", n);//1091567616
	printf("*pFloat的值为：%f\n", *pFloat); //9.0
	return 0;
}*/

/*浮点数存的过程*/

//任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式：
//V = (−1)的S次方 ∗ M ∗ 2的E次方
// S 表⽰符号位，当S = 0，V为正数；当S = 1，V为负数
// M 表⽰有效数字，M是⼤于等于1，⼩于2的
//E 表⽰指数位
//⼗进制的5.0，写成⼆进制是 101.0 ，相当于(-1)的0次方*1.01*2^2 。
//s=0,m=1.01,e=2
//对于32位的浮点数(float)，最⾼的1位存储符号位S，接着的8位存储指数E，剩下的23位存储有效数字M
//对于64位的浮点数(double)，最⾼的1位存储符号位S，接着的11位存储指数E，剩下的52位存储有效数字M

//int main()
//{
//	float f = 5.5;
//	//101.1
//	//(-1)^0 * 1.011 * 2^2
//	//s = 0
//	//e = 2 +127 -->129
//	//m = 1.011
//	//0 10000001 01100000000000000000000
//	//0100 0000 1011 0000 0000000000000000
//	//40 b0 00 00
//	return 0;
//}

/*浮点数取的过程*/
//指数E从内存中取出还可以再分成三种情况：
//E不全为0或不全为1（常规情况）
// 浮点数就采⽤下⾯的规则表⽰，即指数E的计算值减去127（或1023），得到真实值，再将有效
//数字M前加上第⼀位的1。

//E全为0
// 浮点数的指数E等于1-127（或者1-1023）即为真实值，有效数字M不再加上第⼀位的1，⽽是还
//原为0.xxxxxx的⼩数。这样做是为了表⽰±0，以及接近于0的很⼩的数字。

//E全为1
//如果有效数字M全为0，表⽰±⽆穷⼤（正负取决于符号位s）；


